package com.self.finance.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.RandomUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CreateCache;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.self.admin.dto.AdminBankDto;
import com.self.admin.feign.AdminBankServiceFeign;
import com.self.common.model.Config;
import com.self.common.service.ConfigService;
import com.self.finance.constant.FinanceConstant;
import com.self.finance.mapper.CashRechargeAuditRecordMapper;
import com.self.finance.model.CashRechargeAuditRecord;
import com.self.finance.model.Coin;
import com.self.finance.param.CashParam;
import com.self.finance.service.AccountService;
import com.self.finance.service.CoinService;
import com.self.finance.vo.CashTradeVo;
import com.self.member.dto.UserDto;
import com.self.member.feign.UserFeignClient;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.self.finance.model.CashRecharge;
import com.self.finance.mapper.CashRechargeMapper;
import com.self.finance.service.CashRechargeService;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

@Service
public class CashRechargeServiceImpl extends ServiceImpl<CashRechargeMapper, CashRecharge> implements CashRechargeService{


    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private ConfigService configService;

    @Autowired
    private AdminBankServiceFeign adminBankServiceFeign;

    @Autowired
    private CoinService coinService;

    @Autowired
    private Snowflake snowflake;

    @CreateCache(name = "CASH_RECHARGE_LOCK:")
    private Cache<String,String> lockCache;
    @Autowired
    private CashRechargeAuditRecordMapper cashRechargeAuditRecordMapper;
    @Autowired
    private AccountService accountService;
    /**
     * 条件分页查询
     *
     * @param page      分页参数
     * @param coinId    币种的ID
     * @param userId    用户的Id
     * @param userName  用户的名称
     * @param mobile    用户的手机号
     * @param status    审核的状态
     * @param numMin    充值数量的最小值
     * @param numMax    充值数量的最大值
     * @param startTime 充值的开始时间
     * @param endTime   充值数量的结束时间
     * @return
     */
    @Override
    public Page<CashRecharge> findByPage(Page<CashRecharge> page, Long coinId,
                                         Long userId, String userName, String mobile,
                                         Byte status, String numMin, String numMax,
                                         String startTime, String endTime) {
        LambdaQueryWrapper<CashRecharge> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        Map<Long, UserDto> basicUsersByUsername = null;
        if (userId != null || StringUtils.isNotEmpty(userName) || StringUtils.isNotEmpty(mobile)){
            //查询用户名和真实姓名
            basicUsersByUsername = userFeignClient.getBasicUsersByUsername(userId == null ? null : Arrays.asList(userId), userName, mobile);
            if (!CollectionUtils.isEmpty(basicUsersByUsername)){
                lambdaQueryWrapper.in(!CollectionUtils.isEmpty(basicUsersByUsername.keySet()),CashRecharge::getUserId,basicUsersByUsername.keySet());
            }
        }
        lambdaQueryWrapper.eq(status != null,CashRecharge::getStatus,status)
                          .between(!StringUtils.isEmpty(numMin) || !StringUtils.isEmpty(numMax),
                                  CashRecharge::getNum,
                                  StringUtils.isEmpty(numMin)? BigDecimal.ZERO:new BigDecimal(numMin),
                                  StringUtils.isEmpty(numMax)? BigDecimal.ZERO:new BigDecimal(numMax))
                          .between(!StringUtils.isEmpty(startTime) || !StringUtils.isEmpty(endTime),
                                  CashRecharge::getCreated,
                                  StringUtils.isEmpty(startTime)? DateUtil.beginOfDay(new Date()).toJdkDate() :DateUtil.parse(startTime).toJdkDate(),
                                  StringUtils.isEmpty(endTime)? DateUtil.endOfDay(new Date()).toJdkDate() :DateUtil.parse(endTime).toJdkDate());

        Page<CashRecharge> rechargePage = page(page, lambdaQueryWrapper);
        if (rechargePage == null || CollectionUtils.isEmpty(rechargePage.getRecords())){
            return page;
        }
        if (CollectionUtils.isEmpty(basicUsersByUsername)){
            List<Long> collect = rechargePage.getRecords().stream().map(CashRecharge::getUserId).collect(Collectors.toList());
            basicUsersByUsername = userFeignClient.getBasicUsersByUsername(collect, null, null);
        }
        Map<Long, UserDto> finalBasicUsersByUsername = basicUsersByUsername;
        rechargePage.getRecords().forEach(x -> {
            if (!CollectionUtils.isEmpty(finalBasicUsersByUsername)){
                UserDto userDto = finalBasicUsersByUsername.get(x.getUserId());
                if (userDto != null){
                    x.setUsername(userDto.getUsername());
                    x.setRealName(userDto.getRealName());
                }
            }
        });
        return rechargePage;
    }

    @Override
    public Page<CashRecharge> findUserCashRecharge(Page<CashRecharge> page, Byte status) {
        long userId = Long.parseLong(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
        return page(page, new LambdaQueryWrapper<CashRecharge>()
                .eq(CashRecharge::getUserId, userId)
                .eq(status != null, CashRecharge::getStatus, status)
        );
    }

    @Override
    public CashTradeVo buy(CashParam cashParam) {
        long userId = Long.parseLong(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
        //校验参数最小充值量
        checkCashParam(cashParam);
        //获取银行卡
        List<AdminBankDto> allAdminBanks = adminBankServiceFeign.getAllAdminBanks();
        Assert.notEmpty(allAdminBanks,"银行卡获取不到");
        AdminBankDto adminBankDto = allAdminBanks.get(0);
        //生成订单号，参考号
        String orderNo = snowflake.nextIdStr();
        String remark = RandomUtil.randomNumbers(6);
        //查询币种
        Coin coin = coinService.getById(cashParam.getCoinId());
        Assert.notNull(coin,"币种不存在");
        //cashParam.getMum()这是前端给我们的金额,前端可能因为浏览器的缓存导致价格不准确,因此,我们需要在后台进行计算
        Config buyGCNRate = configService.findByCode(FinanceConstant.CONFIG_CODE_CNY2USDT);
        BigDecimal realMum = cashParam.getMum().multiply(new BigDecimal(buyGCNRate.getValue())).setScale(2, RoundingMode.HALF_UP);
        // 4 在数据库里面插入一条充值的记录
        CashRecharge cashRecharge = new CashRecharge();
        cashRecharge.setUserId(userId);
        // 银行卡的信息
        cashRecharge.setName(adminBankDto.getName());
        cashRecharge.setBankName(adminBankDto.getBankName());
        cashRecharge.setBankCard(adminBankDto.getBankCard());

        cashRecharge.setTradeno(orderNo);
        cashRecharge.setCoinId(cashParam.getCoinId());
        cashRecharge.setCoinName(coin.getName());
        cashRecharge.setNum(cashParam.getNum());
        cashRecharge.setMum(realMum); // 实际的交易金额
        cashRecharge.setRemark(remark);
        cashRecharge.setFee(BigDecimal.ZERO);
        cashRecharge.setType(FinanceConstant.PAY_TYPE_LINEPAY); // 在线支付
        cashRecharge.setStatus((byte) 0); // 待审核
        cashRecharge.setStep((byte) 1);// 第一步

        boolean save = save(cashRecharge);
        if (save) {
            // 5 返回我们的成功对象
            CashTradeVo cashTradeVo = new CashTradeVo();
            // 我们收户的银行卡信息
            cashTradeVo.setAmount(realMum);
            cashTradeVo.setStatus((byte)0);
            cashTradeVo.setName(adminBankDto.getName());
            cashTradeVo.setBankName(adminBankDto.getBankName());
            cashTradeVo.setBankCard(adminBankDto.getBankCard());
            cashTradeVo.setRemark(remark);
            return cashTradeVo;
        }
        return null;
    }

    @Override
    public boolean cashRechargeAudit(CashRechargeAuditRecord cashRechargeAuditRecord) {
        long userId = Long.parseLong(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
        //审核成功后要进行充值，需要要用分布式锁
        boolean b = lockCache.tryLockAndRun(cashRechargeAuditRecord.getId() + "", 300, TimeUnit.SECONDS, () -> {
            Long recordId = cashRechargeAuditRecord.getId();
            CashRecharge cashRecharge = getById(recordId);
            Assert.notNull(cashRecharge,"充值记录不存在");
            Assert.isTrue(cashRechargeAuditRecord.getStatus() != 1,"充值记录已经审核通过");
            CashRechargeAuditRecord auditRecord = new CashRechargeAuditRecord();
            auditRecord.setAuditUserId(userId);
            auditRecord.setRemark(cashRechargeAuditRecord.getRemark());
            auditRecord.setStatus(cashRechargeAuditRecord.getStatus());
            Integer step = cashRechargeAuditRecord.getStep()+1;
            auditRecord.setStep(step.byteValue());
            //保存审核记录
            int insert = cashRechargeAuditRecordMapper.insert(auditRecord);
            Assert.isTrue(insert !=0,"保存审核记录失败");
            if (cashRechargeAuditRecord.getStatus() == 2){//拒绝
                updateById(cashRecharge);
            }else{
                //审核通过，给账户充值
               boolean flag = accountService.transferAccountAmount(userId,cashRecharge.getUserId(),cashRecharge.getCoinId(),cashRecharge.getId(), cashRecharge.getNum(),
                        cashRecharge.getFee(),"recharge_into",Byte.parseByte("1"),cashRecharge.getRemark());
               if (flag){
                   cashRecharge.setLastTime(new Date());
                   updateById(cashRecharge);
               }
            }
        });
        return b;
    }


    private void checkCashParam(CashParam cashParam) {
        BigDecimal num = cashParam.getNum(); // 现金充值的数量
        Config withDrowConfig = configService.findByCode(FinanceConstant.CONFIG_CODE_WITH_DROW);
        String value = withDrowConfig.getValue();
        BigDecimal minRecharge = new BigDecimal(value);
        if (num.compareTo(minRecharge) < 0) {
            throw new IllegalArgumentException("充值数量太小");
        }
    }
}
